#pragma once

#define EXCITE_THRESHOLD 30
#define EXCITE_MAX_THRESHOLD 33
#define EXCITE_SPEED_UP 3
#define CALM_COUNT_THRESHOLD 30
#define SIGNAL_MAX 1200
#define SIGNAL_MIN 0
#define SIGNAL_INIT 600

#define SIGNAL_BUF_SIZE 10
#define EMOTION_BUF_SIZE 10

typedef enum{
	NERVE_TYPE_LEFT_TO_RIGHT,
	NERVE_TYPE_RIGHT_TO_LEFT,
}NERVE_ARROW_TYPE;

typedef enum{
	NERVE_TYPE_INSIDE,
	NERVE_TYPE_AT_LEFT_BOUND,
	NERVE_TYPE_AT_RIGHT_BOUND,
}NERVE_POS_TYPE;

class CNerveArray;

class CNerve
{
public:
	CNerve( NERVE_ARROW_TYPE a_tp, NERVE_POS_TYPE pos_tp, int pos_x, int pos_y ) :
	  	m_nLeftInputSignal(SIGNAL_INIT),
		m_nRightInputSignal(SIGNAL_INIT),
		m_nLeftOutputSignal(SIGNAL_INIT),
		m_nRightOutputSignal(SIGNAL_INIT),
		m_nLeftInputSignal_Buf(SIGNAL_INIT),
		m_nRightInputSignal_Buf(SIGNAL_INIT),
		m_pLeftInputNerve(NULL),
		m_pRightInputNerve(NULL),
		m_pLeftOutputNerve(NULL),
		m_pRightOutputNerve(NULL),
		m_arrow_type(a_tp),
		m_pos_type(pos_tp),
		m_pos_x(pos_x),
		m_pos_y(pos_y),
		m_excitability(0),
		m_calm_count(0),
		m_excite_sig_count(0)
		{
			for( int i=0; i<SIGNAL_BUF_SIZE; i++ )
				m_sig_buf[i] = 0;
			for( int i=0; i<EMOTION_BUF_SIZE; i++ )
			m_emotion_buf[i] = 0;
		};

	virtual ~CNerve(void) {};

	int Save( unsigned char* pNerveMem );
	bool Load( unsigned char* pNerveMem, CNerveArray* pArray );

	int GetMemSize( void ) { return 34; };
	
	void LeftInput( int nSignal ) {m_nLeftInputSignal_Buf = nSignal;};
	void RightInput( int nSignal ) {m_nRightInputSignal_Buf = nSignal;};

	void ReadSignal( int& nLeftIn, int& nRightIn, int& nLeftOut, int& nRightOut ) {nLeftIn=m_nLeftInputSignal; nRightIn=m_nRightInputSignal; nLeftOut=m_nLeftOutputSignal; nRightOut = m_nRightOutputSignal; };

	int GetOutput( void ) { return m_nLeftOutputSignal;};

	virtual bool PrepareProcess( void );
	virtual bool ProcessSignal( void );

	//virtual void Inspire( void );
	//virtual void Punish( void );

	void GetPosition( int& x, int& y ) {x=m_pos_x; y=m_pos_y;};

	bool LO_ConnectToLeft( CNerve* pNerve ) 
	{
		if( (m_pos_type == NERVE_TYPE_AT_RIGHT_BOUND && m_arrow_type == NERVE_TYPE_LEFT_TO_RIGHT) || (m_pos_type == NERVE_TYPE_AT_LEFT_BOUND && m_arrow_type == NERVE_TYPE_RIGHT_TO_LEFT) )
			return false;
		if( pNerve->LeftConnectIn(this) )
			m_pLeftOutputNerve=pNerve;
		else
			return false;
		m_bLO_to_LI = true;
		return true;
	};
	bool LO_ConnectToRight( CNerve* pNerve ) 
	{
		if( (m_pos_type == NERVE_TYPE_AT_RIGHT_BOUND && m_arrow_type == NERVE_TYPE_LEFT_TO_RIGHT) || (m_pos_type == NERVE_TYPE_AT_LEFT_BOUND && m_arrow_type == NERVE_TYPE_RIGHT_TO_LEFT) )
			return false;
		if( pNerve->RightConnectIn(this) )
			m_pLeftOutputNerve=pNerve;
		else
			return false;
		m_bLO_to_LI = false;
		return true;
	};
	bool RO_ConnectToLeft( CNerve* pNerve ) 
	{
		if( pNerve->LeftConnectIn(this) )
			m_pRightOutputNerve=pNerve;
		else
			return false;
		m_bRO_to_LI = true;
		return true;
	};
	bool RO_ConnectToRight( CNerve* pNerve ) 
	{
		if( pNerve->RightConnectIn(this) )
			m_pRightOutputNerve=pNerve;
		else
			return false;
		m_bRO_to_LI = false;
		return true;
	};

	bool LeftConnectIn( CNerve* pNerve ) 
	{ 
		if( (m_pos_type == NERVE_TYPE_AT_LEFT_BOUND && m_arrow_type == NERVE_TYPE_LEFT_TO_RIGHT) || (m_pos_type == NERVE_TYPE_AT_RIGHT_BOUND && m_arrow_type == NERVE_TYPE_RIGHT_TO_LEFT) )
			return false;
		else
			m_pLeftInputNerve=pNerve;
		return true;
	};
	bool RightConnectIn( CNerve* pNerve ) {m_pRightInputNerve=pNerve;return true;};

public:
	CNerve* m_pLeftInputNerve;
	CNerve* m_pRightInputNerve;
	CNerve* m_pLeftOutputNerve;
	CNerve* m_pRightOutputNerve;
	bool m_bLO_to_LI; //true for left out connected to left in, otherwise connected to right in
	bool m_bRO_to_LI; //true for right out connected to left in, otherwise connected to right in

protected:
	int m_nLeftInputSignal;
	int m_nRightInputSignal;
	int m_nLeftOutputSignal;
	int m_nRightOutputSignal;

	int m_nLeftInputSignal_Buf;
	int m_nRightInputSignal_Buf;

	NERVE_ARROW_TYPE m_arrow_type;
	NERVE_POS_TYPE m_pos_type;
	int m_pos_x;
	int m_pos_y;
	int m_excitability;
	int m_calm_count;

	int m_sig_buf[SIGNAL_BUF_SIZE];
	int m_emotion_buf[EMOTION_BUF_SIZE];

	int m_excite_sig_count;

	void CheckEmotion( void );
};

class CNerveArray
{
public:
	CNerveArray(void):
	m_nWidth(0),
	m_nDeep(0),
	m_nInputGroupWidth(0),
	m_nOutputGroupWidth(0),
	m_pNerveArray(NULL),
	m_nRunSteps(0)
	{
	};
	virtual ~CNerveArray(void)
	{
		if(m_pNerveArray)
		{
			for( int i=0; i<m_nWidth*m_nDeep; i++ )
				delete m_pNerveArray[i];
			delete m_pNerveArray;
		}
	};

	void BuildArray( int nWidth, int nDeep, int nInputGroupWidth, int nOutputGroupWidth );

	virtual CNerve* CreateNerve( NERVE_ARROW_TYPE a_tp, NERVE_POS_TYPE pos_tp, int nPos_x, int nPos_y )
	{return new CNerve( a_tp, pos_tp, nPos_x, nPos_y ); }; 

	void OneStepProcess( int* inputSignals, int* outputSignals ); 

	//void Inspire( void );
	//void Punish( void );

	CNerve** GetNerves( int& nWidth, int& nDeep ) {return m_pNerveArray;};

	CNerve* GetNerve( int pos_x, int pos_y ) { return m_pNerveArray[pos_x+pos_y*m_nDeep]; };

	int Save( unsigned char* pNerveArrayMem );
	bool Load( unsigned char* pNerveArrayMem );

protected:
	int m_nWidth;
	int m_nDeep;
	int m_nInputGroupWidth;
	int m_nOutputGroupWidth;
	int m_nRunSteps;
	CNerve** m_pNerveArray;
};

class CBrain
{
public:
	CBrain(void);
	virtual ~CBrain(void);

	//void Create( int nMsgCount, int nActionCount, SMsgPriority *MsgPriority );
	//void Think( CMsgArray *inputMsgs, CMsgArray *outputMsgs );

	void Inspire( void );
	void Punish( void );

	CBrain* Clone( void );
	CBrain* Breed( CBrain& wife );

	CNerveArray* GetNerveArray( void ) { return m_pNerveArray;};
private:
	int m_nMsgCount;
	int m_nActionCount;

	CNerveArray *m_pNerveArray;
};
